#include "DumbAi.h"
#include <cassert>

DumbAi::DumbAi(player_t whoIAm){
	m_moving = false;
	if(whoIAm.isP1()){
		m_targetDirection = vector_t(-1,0);
	}else{
		m_targetDirection = vector_t(1,0);
	}
}
int DumbAi::getBranchScore(const GameEngine& ge,const vector_t& wishedDir){
	vector_t v;bool b;
	return getBranchScore(ge,wishedDir,v,b);
}
// Returns the best position that can be attained from this branch.
int DumbAi::getBranchScore(GameEngine ge,const vector_t& wishedDir,vector_t& bestMove, bool& reachedEnd){
	vector_t currentDir;
	int currentScore;
	int bestScore = -Board::w;
	bestMove = wishedDir.inverted()*Board::w;
	position_t ballPos = ge.getBoard().getBallPosition();
	GameInput gi;
	gi.action = gi.MOVEBALL;
	reachedEnd = false;
	int endLoop;
	int i;
	if(wishedDir.x == -1){// To favor the diagonals in a move.
		i = 1;
	}else{
		i = 5;
	}
	endLoop = 8+i;
	for(;i<endLoop;++i){// Iterating trough directions to find the best.
		GameEngine currentGameEngine(ge);
		currentDir = vector_t::iterateDirections(i);
		// If there is a jump this way.
		if(ge.getBoard().getCase(ballPos+currentDir)==Board::CASE_PAWN){
			// Brute forcing to get the jump length.
			int currentJumpLength = 1;
			bool lastInputFailed = true;
			while(currentJumpLength<Board::w && lastInputFailed){
				++currentJumpLength;
				gi.place = ballPos + (currentDir*currentJumpLength);
				currentGameEngine.step(gi);
				lastInputFailed = currentGameEngine.getLastInputFailed();
			}
			// Failed to jump because we went out of the board.
			if(lastInputFailed){
				currentScore = 0;
				if(currentScore>bestScore){
					bestScore = currentScore;
					bestMove = vector_t(0,0);
				}
			}else{// We jumped. Good. Except if it was on the wrong direction.
				player_t winner;
				// Stoping if winning move.
				if(currentGameEngine.getBoard().onGoalZone(currentGameEngine.getBoard().getBallPosition())
					&&currentDir.x == wishedDir.x){
					reachedEnd = true;
					currentScore = 100000;// We are good.
				}else{
					vector_t v;
					currentScore =
						currentDir.x*currentJumpLength*wishedDir.x
						+getBranchScore(currentGameEngine,wishedDir,v,reachedEnd);
				}
				if(currentScore>bestScore){
					bestScore = currentScore;
					bestMove = currentDir*currentJumpLength;
				}else{
					printf("bad luck\n");
				}
				if(reachedEnd){
					return bestScore; //Anyway, we are in the good goal.
				}
			}
		}else{// Not m_moving.
			currentScore = 0;
			if(currentScore>bestScore){
				bestScore = currentScore;
				bestMove = vector_t(0,0);
			}
		}
		// Note that the max update can't be set there because of the bestMove.
	}// End foreach directions.
	return bestScore;
}

/*
 * Attempts to do the best ball move, and modifies the gameEngine
 * with this move.
 * \return true if the goal was reached.
 */
bool DumbAi::dumbBallMove(GameEngine& ge, const vector_t& direction){
	int maxScoreWithJump;
	vector_t bestMove;
	GameInput input;
	bool reachedGoal;
	input.action = GameInput::MOVEBALL;
	maxScoreWithJump = getBranchScore(ge,direction,bestMove,reachedGoal);
	while(maxScoreWithJump>0&& !reachedGoal){
		input.place = ge.getBoard().getBallPosition()+bestMove;
		ge.step(input);
		assert(!ge.getLastInputFailed());
		maxScoreWithJump = getBranchScore(ge,direction,bestMove,reachedGoal);
	}
	// Now the board has the move done.
	return reachedGoal;
}

position_t DumbAi::bestPawnPosition(
	const GameEngine& start,
	const GameEngine& afterMoves,
	const vector_t& direction){
	position_t ballPos =
		afterMoves.getBoard().getBallPosition();
	// Returning a position not yet occupied in the current game.
	if(start.getBoard().isPawnPlacable(ballPos+direction)){
		return ballPos+direction;
	}else if(
		start.getBoard().isPawnPlacable(
			ballPos+direction+vector_t(0,1))
		&& start.getBoard().isPawnPlacable(
			ballPos+direction*2+vector_t(0,2))){
		return ballPos+direction+vector_t(0,1);
	}else if(
		start.getBoard().isPawnPlacable(
			ballPos+direction+vector_t(0,-1))
		&& start.getBoard().isPawnPlacable(
			ballPos+direction*2+vector_t(0,-2))){
		return ballPos+direction+vector_t(0,-1);
	}else{
		assert(false);// Forgot this case.
		return position_t();
	}
}

GameInput DumbAi::play(const GameEngine& ge){
	vector_t bestMove;
	position_t ballPos = ge.getBoard().getBallPosition();
	GameInput ret;
	bool reachedGoal;
	int branchScore = getBranchScore(ge,m_targetDirection,bestMove,reachedGoal);
	if(branchScore<=0 && !reachedGoal){// No good move.
		if(m_moving){
			ret.action = GameInput::ENDMOVE;
			m_moving = false;
		}else{// Create oportunity for a good move next turn.
			GameEngine gameEngineAfterMoves(ge);
			// The ennemy turn.
			reachedGoal = dumbBallMove(
				gameEngineAfterMoves,
				m_targetDirection.inverted());
			vector_t move;
			// Don't let the ennemy trick you. To replace by a new parameter in dumbBallMove.
			if(reachedGoal){
				getBranchScore(ge,m_targetDirection.inverted(),move,reachedGoal);
				ret.action = GameInput::MOVEBALL;
				ret.place = ballPos + move;
				printf("Avoiding getting tricked.\n");
			}else{
				// Your turn, that will be improved with the pawn that you will place.
				reachedGoal = dumbBallMove(
					gameEngineAfterMoves,
					m_targetDirection);
				if(reachedGoal){// Play the ennemy turn then ours.
					getBranchScore(ge,m_targetDirection.inverted(),move,reachedGoal);
					ret.action = GameInput::MOVEBALL;
					ret.place = ballPos + move;
				}else{// Improve for when the ennemy will play.
					ret.action = GameInput::PLACEPAWN;
					ret.place = bestPawnPosition(ge,gameEngineAfterMoves,m_targetDirection);
					printf("Anticipating next ennemy move.\n");
				}
			}
		}
	}else{// Should better place a pawn, or move if the other player would move.
		// Anticipating other player move.
		bool ennemyWillMove =
			getBranchScore(ge,m_targetDirection.inverted())>0;
		if(m_moving || ennemyWillMove){
			// Do the move. No time to think. Maybe more thinking later.
			ret.action = GameInput::MOVEBALL;
			ret.place = ballPos+bestMove;
			m_moving = true;
		}else{// If the other player can't play yet.
			if(reachedGoal){
				ret.action = GameInput::MOVEBALL;
				ret.place = ballPos+bestMove;
				printf("To the win !.\n");
			}else{
				GameEngine gameEngineAfterMoves(ge);
				ret.action = GameInput::PLACEPAWN;
				// Your turn, that will be improved with the pawn that you will place.
				dumbBallMove(
					gameEngineAfterMoves,
					m_targetDirection);
				ret.place = bestPawnPosition(ge,gameEngineAfterMoves,m_targetDirection);
				printf("Anticipated.\n");
			}
		}
	}
	return ret;
}